package com.lagou.edu.spring.core.processors;

import com.lagou.edu.spring.annotations.Autowired;
import com.lagou.edu.spring.annotations.Bean;
import com.lagou.edu.spring.core.ApplicationContext;
import com.lagou.edu.spring.core.BeanDefinition;
import com.lagou.edu.spring.core.BeanDefinitionRegistry;
import com.lagou.edu.spring.core.BeanFactoryPostProcessor;

import java.lang.reflect.Method;
import java.lang.reflect.Parameter;
import java.util.Arrays;
import java.util.List;
import java.util.stream.Collectors;

/**
 * Bean注解解析器
 * 该处理器会获取到所有的beanDefinition，再依次扫描这些类中的方法有没有使用@Bean注解的方法，
 * 如果有，则将这个方法包装成beanDefinition注册到容器中
 *
 * @author 大犬
 * @date 2020/4/2
 */
public class BeanParserProcessor implements BeanFactoryPostProcessor {

    @Override
    public void preProcessBeanFactory(BeanDefinitionRegistry beanDefinitionRegistry, ApplicationContext applicationContext) {
        List<BeanDefinition> allBeanDefinitions = beanDefinitionRegistry.getAllBeanDefinitions();
        allBeanDefinitions.forEach(beanDefinition -> parseBean(beanDefinition, applicationContext, beanDefinitionRegistry));
    }

    private void parseBean(BeanDefinition beanDefinition, ApplicationContext applicationContext, BeanDefinitionRegistry beanDefinitionRegistry) {
        Class parentBeanType = beanDefinition.getBeanType();
        Method[] declaredMethods = parentBeanType.getDeclaredMethods();
        for (Method declaredMethod : declaredMethods) {
            Bean bean = declaredMethod.getAnnotation(Bean.class);
            if (bean == null) {
                continue;
            }
            String parentName = beanDefinition.getBeanName();
            // 父类跳过增强
            beanDefinition.setSkipWrapper(true);
            // 提前创建好父bean
            applicationContext.getBean(parentName);
            declaredMethod.setAccessible(true);
            // 获取bean名称
            String beanName = getBeanName(bean, declaredMethod);
            // 获取bean类型
            Class<?> beanType = declaredMethod.getReturnType();
            // 获取bean依赖的所有beanName
            List<String> dependenciesBeanNames = getBeanDependenciesBeanNames(declaredMethod);
            // 封装成BeanDefinition对象
            BeanDefinition newBeanDefinition = new BeanDefinition();
            newBeanDefinition.setBeanType(beanType);
            newBeanDefinition.setBeanName(beanName);
            newBeanDefinition.setSkipWrapper(true);
            newBeanDefinition.setParentBeanName(parentName);
            newBeanDefinition.setBeanCreateMethod(declaredMethod);
            newBeanDefinition.setCreateMethodDependenceBeanNames(dependenciesBeanNames);
            // 注册到注册中心
            beanDefinitionRegistry.register(beanName, newBeanDefinition);
        }
    }

    private List<String> getBeanDependenciesBeanNames(Method declaredMethod) {
        Parameter[] parameters = declaredMethod.getParameters();
        if (parameters == null) {
            return null;
        }
        return Arrays.stream(parameters)
                .map(this::getParameterBeanName)
                .collect(Collectors.toList());
    }

    private String getParameterBeanName(Parameter parameter) {
        Autowired autowired = parameter.getAnnotation(Autowired.class);
        String parameterBeanName;
        if (autowired == null || autowired.value().isEmpty()) {
            parameterBeanName = parameter.getType().getSimpleName();
        } else {
            parameterBeanName = autowired.value();
        }
        return parameterBeanName;
    }

    private String getBeanName(Bean bean, Method declaredMethod) {
        String beanName = bean.value();
        if (beanName.isEmpty()) {
            // 如果bean注解中没有指定bean的名称，则使用方法名作为bean的名称
            beanName = declaredMethod.getName();
        }
        return beanName;
    }
}
